package logger

import (
	"fmt"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
	"niu-video/config"
	"os"
	"time"
)

// 全局日志变量
var Log *zap.Logger

/**
 * 初始化日志
 * filename 日志文件路径
 * level 日志级别
 * maxSize 每个日志文件保存的最大尺寸 单位：M
 * maxBackups 日志文件最多保存多少个备份
 * maxAge 文件最多保存多少天
 * compress 是否压缩
 * serviceName 服务名
 * 由于zap不具备日志切割功能, 这里使用lumberjack配合
 */
func InitLogger() {
	// 获取编码器
	encoder := getEncoder()
	// 日志级别
	highPriority := zap.LevelEnablerFunc(func(level zapcore.Level) bool {
		return level >= zap.ErrorLevel
	})
	lowPriority := zap.LevelEnablerFunc(func(level zapcore.Level) bool {
		// 当yml配置中的等级大于Error时，lowPriority级别日志停止记录
		return config.Conf.Logs.Level < 2 && level < zap.ErrorLevel && level >= zap.DebugLevel
	})
	// 两个文件夹的 writeSyncer
	infoFileWriteSyncer := getLogWriter("info")
	errorFileWriteSyncer := getLogWriter("error")

	// 第三个及之后的参数为写入文件的日志级别,ErrorLevel模式只记录error级别的日志
	infoFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer, zapcore.AddSync(os.Stdout)), lowPriority)
	errorFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorFileWriteSyncer, zapcore.AddSync(os.Stdout)), highPriority)

	Log = zap.New(zapcore.NewTee(infoFileCore, errorFileCore), zap.AddCaller())
	InfoString("logger", "", "初始化zap日志完成!")
}

// NewConsoleEncoder创建一个编码器，其输出是为人类而不是机器设计的。
// 它以纯文本格式序列化核心日志条目数据(消息、级别、时间戳等)，并将结构化上下文保留为JSON。
func getEncoder() zapcore.Encoder {
	encoderConfig := zapcore.EncoderConfig{
		MessageKey:    "msg",
		LevelKey:      "level",
		TimeKey:       "time",
		NameKey:       "name",
		CallerKey:     "file",
		FunctionKey:   "func",
		StacktraceKey: "stacktrace",
		LineEnding:    zapcore.DefaultLineEnding,
		EncodeLevel:   zapcore.CapitalLevelEncoder,
		EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
			enc.AppendString(t.Format("2006-01-02 15:04:05"))
		},
		EncodeDuration: zapcore.SecondsDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	}
	return zapcore.NewConsoleEncoder(encoderConfig)
}

// 日志记录器
func getLogWriter(file string) zapcore.WriteSyncer {
	now := time.Now()
	infoLogFileName := fmt.Sprintf("%s/%s/%04d-%02d-%02d.log", config.Conf.Logs.Path, file, now.Year(), now.Month(), now.Day())

	// writeSyncer
	return zapcore.AddSync(&lumberjack.Logger{
		Filename:   infoLogFileName,             //日志文件存放目录，如果文件夹不存在会自动创建
		MaxSize:    config.Conf.Logs.MaxSize,    //文件大小限制,单位MB
		MaxAge:     config.Conf.Logs.MaxAge,     //日志文件保留天数
		MaxBackups: config.Conf.Logs.MaxBackups, //最大保留日志文件数量
		LocalTime:  false,
		Compress:   config.Conf.Logs.Compress, //是否压缩处理
	})
}

// LogIf 当 err != nil 时记录 error 等级的日志
func LogIf(err error) {
	if err != nil {
		Log.Error("Error Occurred:", zap.Error(err))
	}
}

// LogWarnIf 当 err != nil 时记录 warning 等级的日志
func LogWarnIf(err error) {
	if err != nil {
		Log.Warn("Error Occurred:", zap.Error(err))
	}
}

// LogInfoIf 当 err != nil 时记录 info 等级的日志
func LogInfoIf(err error) {
	if err != nil {
		Log.Info("Error Occurred:", zap.Error(err))
	}
}

// Debug 调试日志，详尽的程序日志
// 调用示例：
//
//	logger.Debug("Database", zap.String("sql", sql))
func Debug(moduleName string, fields ...zap.Field) {
	Log.Debug(moduleName, fields...)
}

// Info 告知类日志
func Info(moduleName string, fields ...zap.Field) {
	Log.Info(moduleName, fields...)
}

// Warn 警告类
func Warn(moduleName string, fields ...zap.Field) {
	Log.Warn(moduleName, fields...)
}

// Error 错误时记录，不应该中断程序，查看日志时重点关注
func Error(moduleName string, fields ...zap.Field) {
	Log.Error(moduleName, fields...)
}

// Fatal 级别同 Error(), 写完 log 后调用 os.Exit(1) 退出程序
func Fatal(moduleName string, fields ...zap.Field) {
	Log.Fatal(moduleName, fields...)
}

// DebugString 记录一条字符串类型的 debug 日志，调用示例：
//
//	logger.DebugString("SMS", "短信内容", string(result.RawResponse))
func DebugString(moduleName, name, msg string) {
	Log.Debug(moduleName, zap.String(name, msg))
}

func InfoString(moduleName, name, msg string) {
	Log.Info(moduleName, zap.String(name, msg))
}

func WarnString(moduleName, name, msg string) {
	Log.Warn(moduleName, zap.String(name, msg))
}

func ErrorString(moduleName, name, msg string) {
	Log.Error(moduleName, zap.String(name, msg))
}

func FatalString(moduleName, name, msg string) {
	Log.Fatal(moduleName, zap.String(name, msg))
}
